package com.smsc.headend.task.engine.executor.impl.deviceconfig;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.smsc.headend.common.enums.PaymentSwithPreToPostOptGroupEnum;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.dmo.MeterConfigDMO;
import com.smsc.headend.module.asset.entity.DeviceTypeGroup;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.data.dmo.DeviceSAPandKeys;
import com.smsc.headend.module.data.dto.AnsiSubDataItemDTO;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.enums.OperationGroup;
import com.smsc.headend.module.data.enums.OperationName;
import com.smsc.headend.module.protocol.ProtocolType;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.enums.DeviceActionType;
import com.smsc.headend.module.task.enums.SubTaskStatus;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskExecutorException;
import com.smsc.headend.task.engine.executor.TaskExecutor;
import com.smsc.headend.task.engine.facade.DataItemFacade;
import com.smsc.headend.task.engine.handler.impl.ReadProfileCaptureObjectsHandler;
import com.smsc.headend.task.engine.handler.impl.ReadProfilePeriodHandler;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.task.engine.service.ConnectorService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.task.TaskPacketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;

/**
 * 读取Profile CaptureObj Period
 */
@Slf4j
public class ReadProfileObjPrdExecutor implements TaskExecutor {
    @Autowired
    TaskPacketService packetService;
    @Autowired
    ConnectorService connectorService;

    @Autowired
    RedisUtils redisUtils;
    @Autowired
    FeignAssetManagementService assetManagementRemoteService;
    @Autowired
    DataItemFacade dataItemFacade;

    @Autowired
    KafkaTemplate kafkaTemplate;
    @Autowired
    AssetService assetService;
    @Autowired
    TaskDAO taskDAO;

    @Override
    public AtomicTaskState execute(Task task, AtomicTask atomicTask) throws UDISTaskExecutorException {
        Meter meter = getMeter(task, assetService);
        Long modelId = meter.getModelId();
        DeviceTypeGroup deviceTypeGroup = redisUtils.getDeviceTypeGroup(modelId, null, assetManagementRemoteService);
        boolean isAnsi = false;
        if (deviceTypeGroup.getProtocolId() == ProtocolType.ANSI_C12_18_Protocol.getProtocolId()) {
            isAnsi = true;
        }
        MeterConfigDMO meterConfigDMO = task.getMeterId() == null ? null : assetService.getMeterConfigById(task.getMeterId());
        Map<String, Object> paramMap = getParameterMap(task, atomicTask);
        AtomicTaskState atomicState = taskDAO.hashGetAtomicTaskState(atomicTask.getAtomicTaskNo());
        Long profileDataItemId = getProfileDataItemId(paramMap, atomicTask, isAnsi);
        // 抄读 62 lp data source
        if(atomicState != null) {
            profileDataItemId -= 1;
        }
        List<DataItemDTO> dataItemCosemList = dataItemFacade.getDataItemsById(modelId, DeviceActionType.Get, profileDataItemId);
        if (CollectionUtils.isEmpty(dataItemCosemList)) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.DATAITEM_NOT_EXISTS.toString());
        }
        DataItemDTO dataItemDto = dataItemCosemList.get(0);
        DeviceSAPandKeys deviceKeys = assetManagementRemoteService.getMeterCommunicationKey(meter.getMeterId(), dataItemDto.getRoleId());
        byte[] packets;
        if(atomicState == null) {
            Class handlerClass = loadProfileReadHandler(atomicTask);
            atomicState = createAtomicState(handlerClass);
            packets = packetService.buildReadOneAttributePacket(dataItemDto, meterConfigDMO, deviceKeys);
        } else {
            // 计算byteCount
            Integer size = MapUtil.getInt(atomicState.getData(), "NBR_CHNS_SET1");
            int totalSize = 3 * size + 2 * size + 2 * size;
            AnsiSubDataItemDTO ansiSubDataItemDTO = (AnsiSubDataItemDTO) dataItemDto;
            ansiSubDataItemDTO.setByteCount(totalSize);
            packets = packetService.buildReadOneAttributePacket(ansiSubDataItemDTO, meterConfigDMO, deviceKeys);
        }
        log.info(ByteUtils.byteToHexString(packets));
        if(StrUtil.equalsAny(task.getTaskType(), TaskType.ScheduleGet_LP_Period_OBJS.name(), TaskType.ScheduleGet_LB_Capture_OBJS.name(), TaskType.ScheduleGet_Capture_OBJS.name())) {
            connectorService.sendPacket(task, atomicTask, dataItemDto.getRoleId(), true, packets);
        } else {
            connectorService.sendPacket(task, atomicTask, dataItemDto.getRoleId(), task.getIsODR(), packets);
        }
        if (task.getTaskType().equals(TaskType.SwitchMeterPaymentPreToPost.toString())) {
            Map<String, Object> parameterMap = getParameterMap(task, atomicTask);
            Long taskId = MapUtil.getLong(parameterMap, "taskId");
            assetManagementRemoteService.updateStepStatusToPost(taskId, task.getTaskNo(),task.getMeterId(), PaymentSwithPreToPostOptGroupEnum.ON_DEMAND_MONTHLY_BILLING_PROFILE.getOperationGroup().longValue(), SubTaskStatus.Processing.getCode(), SystemClock.now() / 1000, null, null, false);
        }
        return atomicState;
    }

    @Override
    public Boolean handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error {}, ", message);
        if (StrUtil.equalsIgnoreCase(task.getTaskType(), TaskType.Action_DeviceParamConfig_Get.name())) {
            OperationName operationName = OperationName.forId(atomicTask.getOperationId());
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), operationName.getName(), message, 600L, false);
        }
        if (task.getTaskType().equals(TaskType.SwitchMeterPaymentPreToPost.toString())) {
            Map<String, Object> parameterMap = getParameterMap(task, atomicTask);
            Long taskId = MapUtil.getLong(parameterMap, "taskId");
            assetManagementRemoteService.updateStepStatusToPost(taskId, task.getTaskNo(),task.getMeterId(), PaymentSwithPreToPostOptGroupEnum.ON_DEMAND_MONTHLY_BILLING_PROFILE.getOperationGroup().longValue(), SubTaskStatus.Failed.getCode(), SystemClock.now() / 1000, SystemClock.now() / 1000, null, false);
        }
        return true;
    }

    private Long getProfileDataItemId(Map<String, Object> paramMap, AtomicTask atomicTask, boolean isAnsi) {
        OperationName operationName = OperationName.forId(atomicTask.getOperationId());
        switch (operationName) {
            case ReadProfilePeriod:
                return Long.valueOf((paramMap.get("dataItemId") + "104")); // 104是捕获周期属性
            case ReadProfileCaptureObjects:
                // ansi表需先获取table 61的数据
                if (isAnsi) {
                    return Long.valueOf((paramMap.get("dataItemId") + "104"));
                }
                return Long.valueOf((paramMap.get("dataItemId") + "103")); // 103是7类 Object 捕获对象属性
        }
        return null;
    }

    private Class loadProfileReadHandler(AtomicTask atomicTask) {
        OperationName operationName = OperationName.forId(atomicTask.getOperationId());
        switch (operationName) {
            case ReadProfilePeriod:
                return ReadProfilePeriodHandler.class;
            case ReadProfileCaptureObjects:
                return ReadProfileCaptureObjectsHandler.class;
            default:
                return null;
        }
    }
}
